﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Warframe
{
    public class WeaponSystem
    {
        private int enemyLevel;
        private IList<Result> results;
        private IList<Enemy> enemies;
        private IList<Weapon> weapons;
        private IList<Stats> weaponStats;
        private IList<WeaponMod> availableMods;
        private WeaponMod[] currentMods;
        delegate Stats modify(Stats currentStats);

        public WeaponSystem()
        {
            currentMods = new WeaponMod[8];
            initModsList();
            initWeaponsList();
            initEnemiesList();
            weaponStats = new List<Stats>();
            results = new List<Result>();
        }

        public int EnemyLevel { get; set; }

        private void initEnemiesList()
        {
            enemies = new List<Enemy>();
            enemies.Add(new Enemy() { Name = "Infested - Ancient", HitBoxes = new List<Hitbox>() });
            enemies.Last().HitBoxes.Add(new Hitbox() 
                { Name = "Head", 
                Normal = new HitBoxElement() { Vulnerability = 0.75, LevelMod = 0.011 }, 
                ArmorPierce = new HitBoxElement() { Vulnerability = 1.0, LevelMod = 0.0 },
                Electrical = new HitBoxElement() { Vulnerability = 0.0, LevelMod = 0.0 },
                Fire = new HitBoxElement() { Vulnerability = 1.0, LevelMod = 0.0145 },
                Freeze = new HitBoxElement() { Vulnerability = 0.5, LevelMod = 0.0075 }
            });
            enemies.Last().HitBoxes.Add(new Hitbox()
            {
                Name = "Body",
                Normal = new HitBoxElement() { Vulnerability = 0.25, LevelMod = 0.004 },
                ArmorPierce = new HitBoxElement() { Vulnerability = 1.0, LevelMod = 0.0 },
                Electrical = new HitBoxElement() { Vulnerability = 0.0, LevelMod = 0.0 },
                Fire = new HitBoxElement() { Vulnerability = 0.5, LevelMod = 0.007 },
                Freeze = new HitBoxElement() { Vulnerability = 0.25, LevelMod = 0.00375 }
            });
            enemies.Last().HitBoxes.Add(new Hitbox()
            {
                Name = "Legs",
                Normal = new HitBoxElement() { Vulnerability = 1.0, LevelMod = 0.0 },
                ArmorPierce = new HitBoxElement() { Vulnerability = 1.0, LevelMod = 0.0 },
                Electrical = new HitBoxElement() { Vulnerability = 0.0, LevelMod = 0.0 },
                Fire = new HitBoxElement() { Vulnerability = 2.0, LevelMod = 0.0 },
                Freeze = new HitBoxElement() { Vulnerability = 1.0, LevelMod = 0.0 }
            });
        }

        private void initWeaponsList()
        {
            weapons = new List<Weapon>();
            weapons.Add(new Weapon() { BaseDamage = 18, RateOfFire = 8.8, MagazineSize = 60, ReloadTime = 2.5, CritRate = 0.025, CritDamage = 1.50, Name = "Boltor" });
            weapons.Add(new Weapon() { BaseDamage = 9, RateOfFire = 20.0, MagazineSize = 60, ReloadTime = 2.3, CritRate = 0.15, CritDamage = 2.00, Name = "Grakata" });
            weapons.Add(new Weapon() { BaseDamage = 24, RateOfFire = 12.5, MagazineSize = 90, ReloadTime = 4.2, CritRate = 0.01, CritDamage = 1.50, Name = "Gorgon" } );
            weapons.Add(new Weapon() { BaseDamage = 40, RateOfFire = 4.2, MagazineSize = 15, ReloadTime = 2.4, CritRate = 0.075, CritDamage = 1.50, Name = "Latron" });
            weapons.Add(new Weapon() { BaseDamage = 20, RateOfFire = 11.3, MagazineSize = 45, ReloadTime = 2.3, CritRate = 0.05, CritDamage = 1.50, Name = "Braton" });
            weapons.Add(new Weapon() { BaseDamage = 20, RateOfFire = 9.4, MagazineSize = 45, ReloadTime = 2.3, CritRate = 0.075, CritDamage = 1.50, Name = "Braton Vandal" });
            weapons.Add(new Weapon() { BaseDamage = 16, RateOfFire = 6.3, MagazineSize = 60, ReloadTime = 2.2, CritRate = 0.05, CritDamage = 1.50, Name = "MK1-Braton" });
            weapons.Add(new Weapon() { BaseDamage = 21, RateOfFire = 3.3, MagazineSize = 45, ReloadTime = 1.9, CritRate = 0.05, CritDamage = 1.50, Name = "Burston" });
        }

        private void initModsList()
        {
            availableMods = new List<WeaponMod>();
            availableMods.Add(new BaseDamageMod() { Name = "Serration", MaxLevel = 10, ValuePerLevel = 0.15 });
            availableMods.Add(new BaseCritRateMod() { Name = "Point Strike", MaxLevel = 6, ValuePerLevel = 0.25 });
            availableMods.Add(new BaseCritDamageMod() { Name = "Vital Sense", MaxLevel = 6, ValuePerLevel = 0.2 });
            availableMods.Add(new BaseRateOfFireMod() { Name = "Speed Trigger", MaxLevel = 6, ValuePerLevel = 0.1 });
            availableMods.Add(new BaseMagazineSizeMod() { Name = "Magazine Warp", MaxLevel = 6, ValuePerLevel = 0.05 });
            availableMods.Add(new BaseReloadTimeMod() { Name = "Fast Hands", MaxLevel = 6, ValuePerLevel = 0.05 });
            availableMods.Add(new ElementalDamageMod() { Name = "Piercing Hit", MaxLevel = 6, ValuePerLevel = 0.1 });
            availableMods.Add(new ElementalDamageMod() { Name = "Storm Bringer", MaxLevel = 6, ValuePerLevel = 0.15 });
            availableMods.Add(new ElementalDamageMod() { Name = "Cryo Rounds", MaxLevel = 6, ValuePerLevel = 0.15 });
            availableMods.Add(new ElementalDamageMod() { Name = "Hell Fire", MaxLevel = 6, ValuePerLevel = 0.15 });
            availableMods.Add(new MultiShotMod() { Name = "Split Chamber", MaxLevel = 6, ValuePerLevel = 0.15 });
        }

        public Boolean isEmpty(int index)
        {
            if (currentMods[index] == null)
                return true;
            return false;
        }

        public void setMod(WeaponMod mod, int index)
        {
            currentMods[index] = mod;
        }

        public void unSetMod(int index)
        {
            currentMods[index] = null;
        }

        public IList<WeaponMod> getAvailableMods()
        {
            return availableMods;
        }

        public void removeModFromList(WeaponMod mod)
        {
            availableMods.Remove(mod);
        }

        public void returnModToList(int index)
        {
            availableMods.Add(currentMods[index]);
        }

        private void updateWeaponStats()
        {
            weaponStats.Clear();
            Type type;
            foreach(Weapon weapon in weapons)
            {
                weaponStats.Add( new Stats() { Name = weapon.Name, BaseDamage = weapon.BaseDamage, RateOfFire = weapon.RateOfFire, MagazineSize = weapon.MagazineSize, ReloadTime = weapon.ReloadTime, CritRate = weapon.CritRate, CritDamage = weapon.CritDamage } );
                foreach(WeaponMod m in currentMods)
                {
                    if (m != null)
                    {
                        type = m.returnType();
                        if (type != typeof(ElementalDamageMod) && type != typeof(MultiShotMod))
                        {
                            modifyStats(m.modifyDamage, weaponStats.Last());
                        }
                    }
                }
            }
        }

        private void stackElementalDamage()
        {
            Type type;
            foreach (Stats stats in weaponStats)
            {
                foreach (WeaponMod m in currentMods)
                {
                    if (m != null)
                    {
                        type = m.returnType();
                        if (type == typeof(ElementalDamageMod))
                        {
                            modifyStats(m.modifyDamage, stats);
                        }
                    }
                }
            }
        }

        private double calculateDamagePerProjectile(Stats stats, Hitbox hitbox, int enemyLevel)
        {
            //stats.DamagePerProjectile = (stats.BaseDamage + stats.ExtraDamage) * (1 + (stats.CritRate * stats.CritDamage));

            double dmgPerProjectile = ((stats.BaseDamage * (hitbox.Normal.Vulnerability - hitbox.Normal.LevelMod * enemyLevel))
                + (stats.ArmorPierce * (hitbox.ArmorPierce.Vulnerability - hitbox.ArmorPierce.LevelMod * enemyLevel))
                + (stats.Electrical * (hitbox.Electrical.Vulnerability - hitbox.Electrical.LevelMod * enemyLevel))
                + (stats.Fire * (hitbox.Fire.Vulnerability - hitbox.Fire.LevelMod * enemyLevel))
                + (stats.Freeze * (hitbox.Freeze.Vulnerability - hitbox.Freeze.LevelMod * enemyLevel)))
                * (1 + (stats.CritRate * stats.CritDamage));
            Type type;
            foreach (WeaponMod m in currentMods)
            {
                if (m != null)
                {
                    type = m.returnType();
                    if (type == typeof(MultiShotMod))
                    {
                        //modifyStats(m.modifyDamage, stats);
                        dmgPerProjectile = (1 + (6 * 0.15)) * dmgPerProjectile;
                    }
                }
            }
            return dmgPerProjectile;
        }

        private void calculateDPS()
        {
            foreach (Enemy enemy in enemies)
            {
                foreach (Stats stats in weaponStats)
                {
                    /**
                    stats.DPSHead = (stats.DamagePerProjectileInfestedAncientHead * stats.MagazineSize) / (stats.MagazineSize / stats.RateOfFire + stats.ReloadTime);
                    stats.DPSBody = (stats.DamagePerProjectileInfestedAncientBody * stats.MagazineSize) / (stats.MagazineSize / stats.RateOfFire + stats.ReloadTime);
                    stats.DPSLegs = (stats.DamagePerProjectileInfestedAncientLegs * stats.MagazineSize) / (stats.MagazineSize / stats.RateOfFire + stats.ReloadTime);
                     */
                    foreach(Hitbox hitbox in enemy.HitBoxes)
                    {
                        results.Add(new Result() { Weapon = stats.Name, Enemy = enemy.Name, Hitbox = hitbox.Name, DPS = ((calculateDamagePerProjectile(stats, hitbox, EnemyLevel) * stats.MagazineSize) / (stats.MagazineSize / stats.RateOfFire + stats.ReloadTime)) });
                    }
                }
            }
        }

        public IList<Result> getCalculations()
        {
            results.Clear();
            updateWeaponStats();
            stackElementalDamage();
            calculateDPS();
            return results;
        }

        private void modifyStats(modify del, Stats weaponStats)
        {
            del(weaponStats);
        }
    }
}
